from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Callable, Dict, List, Optional, Protocol


@dataclass
class ValidationIssue:
    """Represents a validation issue found in front matter"""

    field: str
    message: str
    level: str = "error"  # error, warning, info


@dataclass
class ValidationResult:
    """Represents the result of front matter validation"""

    is_valid: bool = True
    issues: List[ValidationIssue] = field(default_factory=list)
    validation_time: datetime = field(default_factory=datetime.now)

    def add_issue(self, field: str, message: str, level: str = "error") -> None:
        """Add a validation issue"""
        self.issues.append(ValidationIssue(field, message, level))
        if level == "error":
            self.is_valid = False

    def get_error_messages(self) -> List[str]:
        """Get all error messages as a list of strings"""
        return [issue.message for issue in self.issues if issue.level == "error"]


class FieldValidator(Protocol):
    """Protocol for field validators"""

    def __call__(self, value: Any) -> bool:
        """Validate a field value"""
        ...


@dataclass
class ValidationRule:
    """Represents a validation rule for a front matter field"""

    field_name: str
    validator: Optional[FieldValidator] = None
    error_message: str = ""
    is_required: bool = False

    def validate(self, value: Any, field_exists: bool = True) -> bool:
        """Validate a field value against this rule

        Args:
            value: The value to validate
            field_exists: Whether the field exists in the dictionary

        Returns:
            True if validation passes, False otherwise
        """
        # Check if field is required but missing
        if self.is_required:
            if not field_exists or value is None or value == "":
                return False
            if self.validator:
                return self.validator(value)
            return True
        # If field is not required but exists, validate it
        if field_exists and value is not None and value != "" and self.validator:
            return self.validator(value)
        return True


class FrontMatterValidator:
    """Validator for front matter fields"""

    def __init__(self):
        """Initialize validator with empty rules"""
        self.rules: List[ValidationRule] = []
        self.required_fields: Dict[str, str] = {}
        self.format_rules: Dict[str, tuple[Callable[[Any], bool], str]] = {}

    def add_required_field(self, field_name: str, error_message: str):
        """Add a required field rule

        Args:
            field_name: Name of the required field
            error_message: Error message to show when field is missing
        """
        self.rules.append(
            ValidationRule(
                field_name=field_name, error_message=error_message, is_required=True
            )
        )
        # Add to required_fields dictionary for backward compatibility
        self.required_fields[field_name] = error_message

    def add_format_rule(
        self, field_name: str, validator: Callable[[Any], bool], error_message: str
    ):
        """Add a format validation rule

        Args:
            field_name: Name of the field to validate
            validator: Function that takes a value and returns bool
            error_message: Error message to show when validation fails
        """
        self.rules.append(
            ValidationRule(
                field_name=field_name, validator=validator, error_message=error_message
            )
        )
        # Add to format_rules dictionary for backward compatibility
        self.format_rules[field_name] = (validator, error_message)

    def validate(self, fields: Dict[str, Any]) -> List[str]:
        """Validate fields against registered rules

        Args:
            fields: Dictionary of fields to validate

        Returns:
            List of validation error messages
        """
        errors = []

        for rule in self.rules:
            field_exists = rule.field_name in fields
            field_value = fields.get(rule.field_name, None)
            if not rule.validate(field_value, field_exists):
                errors.append(rule.error_message)

        return errors

    def validate_to_result(self, fields: Dict[str, Any]) -> ValidationResult:
        """Validate fields and return a ValidationResult object

        Args:
            fields: Dictionary of fields to validate

        Returns:
            ValidationResult object containing validation issues
        """
        result = ValidationResult()

        for rule in self.rules:
            field_exists = rule.field_name in fields
            field_value = fields.get(rule.field_name, None)
            if not rule.validate(field_value, field_exists):
                result.add_issue(rule.field_name, rule.error_message)

        return result
